﻿@using Gurux.DLMS.AMI.Client.Pages.Device;
@using Gurux.DLMS.AMI.Module
@using Gurux.DLMS.AMI.Shared
@using Gurux.DLMS.AMI.Shared.DIs

@using Gurux.DLMS.AMI.Shared.Enums
@using Gurux.DLMS.Objects;
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Gurux.DLMS.AMI.Shared.DTOs
@using Gurux.DLMS.AMI.Shared.Rest
@using Microsoft.AspNetCore.SignalR.Client
@using Gurux.DLMS.AMI.Client.Pages.Agent
@using System.Text.Json
@using System.Text;
@using Gurux.DLMS.AMI.Client.Helpers

@attribute [Authorize(Roles = "Admin, TemplateManager")]
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IGXNotifier2 Notifier

@if (Active != null)
{
    <EditForm Model="@Active">
        <DataAnnotationsValidator />
        <ValidationSummary />
        <div class="row">
            <div style="width:100%">
                <div class="form-group">
                    <label>@Properties.Resources.LogicalName</label>
                    <InputText id="name" disabled="disabled" class="form-control"
                               @bind-Value="Active.LogicalName" />
                </div>
                <div class="form-group">
                    <label>@Properties.Resources.Description</label>
                    <InputText id="name" disabled="@IsDeleting" class="form-control"
                               @bind-Value="Active.Name" />
                </div>
                <table class="table table-striped" width="100%">
                    <thead>
                        <tr>
                            <th>Index</th>
                            <th>@Properties.Resources.Name</th>
                            <th>@Properties.Resources.DataType</th>
                            <th>@Properties.Resources.UIDataType</th>
                            <th>@Properties.Resources.Scaler</th>
                            <th>@Properties.Resources.Unit</th>
                            <th>Access rights</th>
                            <th>Expiration time</th>
                            <th width="1%"></th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (var it in GetAttributes())
                        {
                            @if (it.Modified)
                            {
                                <tr>
                                    <td>@it.Index</td>
                                    <td>@it.Name</td>
                                    <td>
                                        <select class="form-select" @bind="@it.DataType">
                                            @foreach (var type in GetDataTypes())
                                            {
                                                <option value="@Convert.ToInt32(type)">@type</option>
                                            }
                                        </select>
                                    </td>
                                    <td>
                                        <select class="form-select" @bind="@it.UIDataType">
                                            @foreach (var type in GetDataTypes())
                                            {
                                                <option value="@Convert.ToInt32(type)">@type</option>
                                            }
                                        </select>
                                    </td>
                                    <td>
                                        @if (IsNumber(it.DataType))
                                        {
                                            <InputNumber step="0.0001"
                                                         class="form-control"
                                                         id="scaler"
                                                         @bind-Value="@it.Scaler" />
                                        }
                                        else
                                        {
                                            <!--Scaler is shown only for number data types.-->
                                        }
                                    </td>
                                    <td>
                                        <InputText class="form-control" id="unit" @bind-Value="@it.Unit" />
                                    </td>
                                    <td>@GetAccessRights(it)</td>
                                    <td><InputNullableDateTimeOffsetTimeSpan @bind-Value="it.ExpirationTime"></InputNullableDateTimeOffsetTimeSpan></td>
                                    <td>
                                        <button type="button" class="btn btn-primary"
                                                @onclick="() => UpdateInstance(it, true)">
                                            @Properties.Resources.Update
                                        </button>
                                        <button type="button" class="btn btn-secondary" @onclick="() => UpdateInstance(it, false)">
                                            @Properties.Resources.Cancel
                                        </button>
                                    </td>
                                </tr>
                            }
                            else
                            {
                                <tr class=@(GetSelectedClass(it)) @onclick="@(()=>RowSelected(it))">
                                    <td>@it.Index</td>
                                    <td><LinkButton Target="@it" OnClick="@OnEdit" Text="@it.Name" /></td>
                                    <td>@GetDataType(it.DataType)</td>
                                    <td>@GetDataType(it.UIDataType)</td>
                                    <td>@it.Scaler</td>
                                    <td>@it.Unit</td>
                                    <td>@GetAccessRights(it)</td>
                                    <td>@GetExpirationTime(it)</td>
                                    @if (_activeAttribute == it)
                                    {
                                        <td>
                                            <ContextMenuController>
                                                <ChildContent>
                                                    <ContextMenuItem Text="@Properties.Resources.Edit" Icon="oi oi-pencil" OnClick="() => OnEdit(it)"></ContextMenuItem>
                                                </ChildContent>
                                            </ContextMenuController>
                                        </td>
                                    }
                                    else
                                    {
                                        <td>&nbsp;&nbsp;&nbsp;</td>
                                    }
                                </tr>
                            }
                        }
                    </tbody>
                </table>

                <table class="table table-striped" width="100%">
                    <thead>
                        <tr>
                            <th>Index</th>
                            <th>@Properties.Resources.Name</th>
                            <th>Access rights</th>
                        </tr>
                    </thead>
                    <tbody>
                        @foreach (var it in GetActions())
                        {
                            <tr>
                                <td>@it.Index</td>
                                <td>@it.Description</td>
                                <td>@it.AccessLevel</td>
                            </tr>
                        }
                    </tbody>
                </table>

                @if (Active.Updated != null)
                {
                    <div class="form-group">
                        <label>@Properties.Resources.Updated</label>
                        <InputDate Type="@InputDateType.DateTimeLocal" id="updated" readonly="readonly" class="form-control"
                                   @bind-Value="Active.Updated" />
                    </div>
                }
                @if (Active.Removed != null)
                {
                    <div class="form-group">
                        <label>@Properties.Resources.Remove</label>
                        <InputDate Type="@InputDateType.DateTimeLocal" id="remove" class="form-control"
                                   @bind-Value="Active.Removed" />
                    </div>
                }
            </div>
        </div>
    </EditForm>
}
@code {
    [CascadingParameter]
    private ObjectTemplates? Parent { get; set; }

    private GXObjectTemplate? _active;

    private GXObjectTemplate? Active
    {
        get
        {
            if (_active != null)
            {
                return _active;
            }
            return Parent?.Active;
        }
    }

    /// <summary>
    /// User action.
    /// </summary>
    [Parameter]
    public string? Action { get; set; }

    private CrudAction action;
    /// <summary>
    /// Selected item.
    /// </summary>
    [Parameter]
    public Guid? Id { get; set; }


    /// <summary>
    /// Get data type.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    private string GetDataType(int value)
    {
        if (value == 0)
        {
            return string.Empty;
        }
        return ((Gurux.DLMS.Enums.DataType)value).ToString();
    }

    private Gurux.DLMS.Enums.DataType[] GetDataTypes()
    {
        return Enum.GetValues<Gurux.DLMS.Enums.DataType>();
    }

    private static bool IsNumber(int value)
    {
        Gurux.DLMS.Enums.DataType dt = (Gurux.DLMS.Enums.DataType)value;
        switch (dt)
        {
            case Enums.DataType.Float32:
            case Enums.DataType.Float64:
            case Enums.DataType.Int16:
            case Enums.DataType.Int32:
            case Enums.DataType.Int64:
            case Enums.DataType.Int8:
            case Enums.DataType.None:
            case Enums.DataType.DeltaInt8:
            case Enums.DataType.DeltaInt16:
            case Enums.DataType.DeltaInt32:
            case Enums.DataType.DeltaUInt8:
            case Enums.DataType.DeltaUInt16:
            case Enums.DataType.DeltaUInt32:
            case Enums.DataType.UInt16:
            case Enums.DataType.UInt32:
            case Enums.DataType.UInt64:
            case Enums.DataType.UInt8:
                return true;
        }
        return false;
    }


    private GXAttributeTemplate? _activeAttribute;

    /// <summary>
    /// List of the modified items.
    /// </summary>
    private Dictionary<GXAttributeTemplate, GXAttributeTemplate> Modified = new Dictionary<GXAttributeTemplate, GXAttributeTemplate>();

    /// <summary>
    /// Get attributes.
    /// </summary>
    /// <returns>Attributes.</returns>
    private IEnumerable<GXAttributeTemplate> GetAttributes()
    {
        List<GXAttributeTemplate> list = new List<GXAttributeTemplate>();
        GXAttributeTemplate? modified;
        if (Active?.Attributes != null)
        {
            foreach (var it in Active.Attributes)
            {
                if (Modified.TryGetValue(it, out modified))
                {
                    list.Add(modified);
                }
                else
                {
                    list.Add(it);
                }
            }
        }
        return list.OrderBy(s => s.Index);
    }

    class GXAction
    {
        public int Index;
        public string? Description;
        public string? AccessLevel;
    }

    /// <summary>
    /// Get action description and access rights.
    /// </summary>
    /// <returns>Attributes.</returns>
    private IEnumerable<GXAction> GetActions()
    {
        var list = new List<GXAction>();
        if (Active?.ObjectType != null && !string.IsNullOrEmpty(Active?.ActionAccessLevels))
        {
            var obj = Gurux.DLMS.GXDLMSClient.CreateObject((Enums.ObjectType)Active.ObjectType) as IGXDLMSBase;
            var names = obj?.GetMethodNames();
            int index = 0;
            if (Active.ActionAccessLevels.StartsWith("0x"))
            {
                //Version 3.
                for (var pos = 2; pos < Active.ActionAccessLevels.Length; pos += 2)
                {
                    var str = Active.ActionAccessLevels.Substring(pos, 2);
                    var value = (Gurux.DLMS.Enums.MethodAccessMode3)Convert.ToInt32(str);
                    GXAction item = new GXAction()
                        {
                            Index = 1 + index,
                            Description = names != null ? names[index] : "",
                            AccessLevel = value.ToString()
                        };
                    list.Add(item);
                    ++index;
                }
            }
            else
            {
                //Version 1.
                foreach (var it in Active.ActionAccessLevels)
                {
                    var value = (Gurux.DLMS.Enums.MethodAccessMode)Convert.ToInt32(it);
                    GXAction item = new GXAction()
                        {
                            Index = 1 + index,
                            Description = names != null ? names[index] : "",
                            AccessLevel = value.ToString()
                        };
                    list.Add(item);
                    ++index;
                }
            }
        }
        return list;
    }

    /// <summary>
    /// Copy the content of the modified item to the original item.
    /// </summary>
    /// <param name="modified">Modified item.</param>
    /// <param name="save">Is value saved.</param>
    protected void UpdateInstance(GXAttributeTemplate modified, bool save)
    {
        //Find key with modified item.
        GXAttributeTemplate? key = null;
        foreach (var it in Modified)
        {
            if (it.Value == modified)
            {
                key = it.Key;
                break;
            }
        }
        if (key == null)
        {
            throw new Exception("Invalid target.");
        }
        if (save)
        {
            ClientHelpers.Copy(modified, key);
        }
        if (Modified.ContainsKey(key))
        {
            Modified.Remove(key);
        }
        key.Modified = false;
    }

    protected string GetSelectedClass(GXAttributeTemplate selected)
    {
        return _activeAttribute != null && selected.Id == _activeAttribute.Id ? "table-info" : "table-striped";
    }
    protected void RowSelected(GXAttributeTemplate selected)
    {
        _activeAttribute = selected;
    }

    public string? IsDeleting
    {
        get
        {
            if (Notifier.Action != CrudAction.Delete)
            {
                return null;
            }
            return "disabled";
        }
    }

    private string GetAccessRights(GXAttributeTemplate template)
    {
        return ((Enums.AccessMode)template.AccessLevel).ToString();
    }

    private string GetExpirationTime(GXAttributeTemplate template)
    {
        string str = "";
        if (template.ExpirationTime != null)
        {
            if (template.ExpirationTime.Value.Year == DateTimeOffset.MaxValue.Year)
            {
                str = "static";
            }
            else
            {
                str = template.ExpirationTime.Value.ToString("HH:mm:ss");
            }
        }
        return str;
    }

    private List<GXScript> Scripts = new();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            Notifier.ProgressStart();
            Notifier.ClearStatus();
            Notifier.Clear();
            if (Active == null && Id != null)
            {
                //Get object template data.
                var tmp = (await Http.GetAsJsonAsync<GetObjectTemplateResponse>(string.Format("api/ObjectTemplate?id={0}", Id)));
                if (tmp?.Item != null)
                {
                    _active = tmp.Item;
                }
                else
                {
                    NavigationManager.NavigateTo("404");
                }
            }
            if (Active == null)
            {
                throw new ArgumentException(Properties.Resources.InvalidTarget);
            }
            action = ClientHelpers.GetAction(Action); if (action == CrudAction.Delete)
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Remove, Icon = "oi oi-trash", OnClick = OnSave });
            }
            else
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Save, Icon = "oi oi-pencil", OnClick = OnSave });
            }
            Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Cancel, Icon = "oi oi-action-undo", OnClick = OnCancel });
            Notifier.UpdateButtons();
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier?.ProgressEnd();
        }
    }

    public void OnEdit(GXAttributeTemplate template)
    {
        GXAttributeTemplate tmp = ClientHelpers.Clone<GXAttributeTemplate>(template);
        Modified.Add(template, tmp);
        tmp.Modified = true;
        StateHasChanged();
    }

    /// <summary>
    /// Save object template.
    /// </summary>
    public async void OnSave()
    {
        try
        {
            if (Active == null)
            {
                throw new Exception(Properties.Resources.InvalidTarget);
            }
            Notifier.ProgressStart();
            Notifier.ClearStatus();
            if (action == CrudAction.Delete)
            {
                await Http.PostAsJson<RemoveAgentResponse>("api/ObjectTemplate/Delete", new RemoveAgent() { Ids = new Guid[] { Active.Id } });
            }
            else
            {
                string address;
                if (action == CrudAction.Create)
                {
                    address = "api/ObjectTemplate/Add";
                }
                else if (action == CrudAction.Update)
                {
                    address = "api/ObjectTemplate/Update";
                }
                else
                {
                    throw new Exception(Properties.Resources.InvalidTarget);
                }
                await Http.PostAsJson<UpdateObjectTemplateResponse>(address, new UpdateObjectTemplate() { ObjectTemplates = new GXObjectTemplate[] { Active } });
            }
            ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier?.ProgressEnd();
        }
    }

    /// <summary>
    /// Cancel update.
    /// </summary>
    private void OnCancel()
    {
        ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
    }
}
